package cn.tedu.nio.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) {
        try {
            // 开启服务器通道
            ServerSocketChannel ssc = ServerSocketChannel.open();
            // 绑定监听端口号
            ssc.bind(new InetSocketAddress(8070));
            // 因为selector针对非阻塞通道进行选择，所以需要将通道设置为非阻塞
            ssc.configureBlocking(false);
            // 开启选择器
            Selector selc = Selector.open();
            // 将服务器注册到选择器上
            ssc.register(selc, SelectionKey.OP_ACCEPT);
            // 实际过程中，服务器一旦开启就不会关闭, 所以用while(true)来模拟服务器一直开启
            while (true) {
                // 需要进行选择, 选择出能触发服务器事件的连接
                selc.select();
                // 无论筛选出多少个通道，最终都只可能触发服务器的accept/read/write事件
                // 确定这次选择之后的通道能触发的事件类型
                // accept/read/write/accept-read/accept-write/read-write
                Set<SelectionKey> keys = selc.selectedKeys();
                // 遍历set，根据触发的事件，来进行不同的处理
                Iterator<SelectionKey> it = keys.iterator();
                while (it.hasNext()) {
                    SelectionKey key = it.next();
                    // 可接受事件
                    if (key.isAcceptable()) {
                        // 从事件中将通道获取出来
                        ServerSocketChannel sscx = (ServerSocketChannel) key.channel();
                        // 接收连接
                        SocketChannel sc = sscx.accept();
                        // 不确定接过来的连接是否非阻塞, selector要求通道非阻塞
                        sc.configureBlocking(false);
                        // 允许连接过来的通道同时允许读写
                        sc.register(selc, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                    }
                    // 可读事件
                    if (key.isReadable()) {
                        // 从事件获取通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        // 准备一个缓冲区用于存储数据
                        ByteBuffer dst = ByteBuffer.allocate(1024);
                        // 读取数据
                        sc.read(dst);
                        // 解析数据
                        System.out.println(new String(dst.array(), 0, dst.position()));
                        // 可读事件已经处理完了, 需要从这个通道上将可读事件移除
                        // key.interestOps() 获取原来所有的事件
                        sc.register(selc, key.interestOps() ^ SelectionKey.OP_READ);
                    }
                    // 可写事件
                    if (key.isWritable()) {
                        // 从事件中获取通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        // 写出数据, 服务器发给客户端
                        sc.write(ByteBuffer.wrap("Hello client".getBytes()));
                        // 可写事件已经处理完了，将可写事件从这个通道移除
                        sc.register(selc, key.interestOps() ^ SelectionKey.OP_WRITE);
                    }
                    // 处理结束, 需要将这个通道移除
                    it.remove();
                }
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}
